package org.agile4j.rpc.server;

import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import net.sf.cglib.reflect.FastClass;
import net.sf.cglib.reflect.FastMethod;

import org.agile4j.rpc.common.bean.RpcRequest;
import org.agile4j.rpc.common.bean.RpcResponse;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;

/**
 *  RPC 服务端处理器（用于处理 RPC 请求）
 *
 * Created by Administrator on 2016/8/16.
 */
public class RpcServerHandler extends SimpleChannelInboundHandler<RpcRequest> {

    private static final Logger LOGGER = LoggerFactory.getLogger(RpcServerHandler.class);

    private final Map<String, Object> handlerMap ;

    public RpcServerHandler(Map<String, Object> handlerMap) {
        this.handlerMap = handlerMap;
    }

    @Override
    public void channelRead0(ChannelHandlerContext ctx, RpcRequest request) throws Exception {
        // 创建并初始化 RPC 响应对象
        RpcResponse response = new RpcResponse() ;
        response.setRequestId(request.getRequestId());
        try {
            Object result = handle(request);
            response.setResult(result);
        } catch (Exception e) {
            LOGGER.error("handle result failure", e);
            response.setException(e);
        }
        // 写入 RPC 响应对象并自动关闭连接
        ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE) ;
    }

    private Object handle(RpcRequest request) throws Exception{
        // 获取服务对象
        String serviceName = request.getInterfaceName() ;
        String serviceVersion = request.getServiceVersion() ;
        if (StringUtils.isNotEmpty(serviceVersion)) {
            serviceName += "-" + serviceVersion ;
        }
        Object serviceBean = handlerMap.get(serviceName);
        if (serviceBean == null) {
            throw new RuntimeException(String.format("can not find service bean by key: %s", serviceName)) ;
        }
        // 获取反射调用所需的参数
        Class<?> serviceClass = serviceBean.getClass() ;
        String methodName =  request.getMethodName() ;
        Class<?>[] parameterTypes = request.getParameterTypes() ;
        Object[] parameters = request.getPrameters() ;

//        Method method = serviceClass.getMethod(methodName, parameterTypes);
//        method.setAccessible(true);
//        return method.invoke(serviceBean, parameters);

        // 为了避免使用 Java 反射带来的性能问题，我们可以使用 CGLib 提供的反射 API
        FastClass serviceFastClass = FastClass.create(serviceClass) ;
        FastMethod serviceFastMethod = serviceFastClass.getMethod(methodName, parameterTypes) ;
        return serviceFastMethod.invoke(serviceBean, parameters) ;
    }

    /**
     * exceptionCaught事件处理方法由一个ExceptionEvent异常事件调用，
     * 这个异常事件起因于Netty的I/O异常或一个处理器实现的内部异常。多数情况下，
     * 捕捉到的异常应当被记录下来，并在这个方法中关闭这个channel通道。
     * 当然处理这种异常情况的方法实现可能因你的实际需求而有所不同，
     * 例如，在关闭这个连接之前你可能会发送一个包含了错误码的响应消息。
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        LOGGER.error("server caught exception", cause);
        ctx.close();
    }
}
